/*
 * @版权所有(C): 1999-2021,西安交大捷普网络科技有限公司
 * @FilePath: \client\baselib\LibJumpXml\JumpXml.h
 * @作者: 岳保保
 * @Date: 2023-01-05 13:40:39
 * @功能描述: jumpxml格式化工具
 */
#ifndef JUMP_XML_H
#define JUMP_XML_H
#include <map>
#include <memory>
#include <fstream>
#include <iostream>
#include "StringUtils.h"
#include "XmlItem.h"
class JumpXml
{
public:
    /**
     * @功能描述: 无参构造函数
     * @入参参数: void
     * @返回值: void
     */
    JumpXml();

    /**
     * @功能描述: 拷贝构造函数
     * @入参参数: 拷贝对象
     * @返回值: void
     */
    JumpXml(const JumpXml& obj);

    /**
     * @功能描述: 赋值函数
     * @入参参数: 拷贝对象
     * @返回值: 赋值对象
     */
    JumpXml& operator=(const JumpXml& obj);

public:

    /**
     * @功能描述: 设置xml root名称
     * @param {string} &xmlStr xml str值
     * @返回值: 成功或者失败
     */
    void setXmlRootName(const std::string& rootName);

    /**
     * @功能描述: 设置xml str 赋值给对象对应的值
     * @param {string} &xmlStr xml str值
     * @返回值: 成功或者失败
     */
    bool setXml(const std::string& xmlStr);

    /**
     * @功能描述: 从文件中获取xml
     * @param {string} &xmlPath xml 路径
     * @返回值: 成功或者失败
     */
    bool loadXmlFromFile(const std::string& xmlPath);

    /**
     * @功能描述: 将对象转为xml str
     * @入参参数: void
     * @返回值: xml str
     */
    const std::string getXml(bool needHeader = true, const std::string& version = "1.0", const std::string& encoding = "UTF-8");

private:
    /*********************************
    @函数描述: 获取上级节点
    @入参 :void
    @返回值 : 上级节点
    **********************************/
    void setXMLDocument(tinyxml2::XMLDocument* preDocument);

    /*********************************
    @函数描述: 获取本级节点
    @入参 :void
    @返回值 : 本级节点
    **********************************/
    void setXMLElement(tinyxml2::XMLElement* curentNode);

protected:
    /**
    * @功能描述: 从xml值中解析成员数据
    * @param {Value} &xmlValue xml值
    * @param {bool} &boolValue 转化的具体的string值
    * @返回值: 成功或者失败
    */
    template<typename T>
    bool element2Val(T& dataValue)
    {
        if (m_xmlelement->GetText())
        {
            dataValue = StringUtils::string2Pod<T>(m_xmlelement->GetText());
            return true;
        }
        // 当节点没有值时,设置值为空
        dataValue = StringUtils::string2Pod<T>("");
        return true;
    }

    /**
    * @功能描述: 从xml值中解析成员数据
    * @param {Value} &xmlValue xml值
    * @param {bool} &boolValue 转化的具体的string值
    * @返回值: 成功或者失败
    */
    template<typename T>
    bool element2Val(const std::string& memberName, T& dataValue)
    {
        const tinyxml2::XMLElement* element = XmlItem::getXmlChildeEleByName(m_xmlelement, memberName);
        if (element == nullptr)
        {
            return false;
        }
        if (element->GetText())
        {
            dataValue = StringUtils::string2Pod<T>(element->GetText());
            return true;
        }
        // 当节点没有值时,设置值为空
        dataValue = StringUtils::string2Pod<T>("");
        return true;
    }

    /**
     * @功能描述: 从xml值中解析成员数据
     * @param {Value} &xmlValue xml值
     * @param {bool} &boolValue 转化的具体的string值
     * @返回值: 成功或者失败
     */
    template<typename T>
    bool attribute2Val(const std::string& attrName, T& dataValue)
    {
        const tinyxml2::XMLAttribute* attr = XmlItem::getXmlChildAttrByName(m_xmlelement, attrName);
        if (attr == nullptr)
        {
            return false;
        }

        if (attr->Value())
        {
            dataValue = StringUtils::string2Pod<T>(attr->Value());
            return true;
        }
        // 当节点没有值时,设置值为空
        dataValue = StringUtils::string2Pod<T>("");
        return false;
    }

    /**
     * @功能描述: 从xml值中解析成员数据
     * @param {Value} &xmlValue xml值
     * @param {bool} &boolValue 转化的具体的string值
     * @返回值: 成功或者失败
     */
    template<typename T>
    bool attribute2Val(const std::string& memberName, const std::string& attrName, T& dataValue)
    {
        tinyxml2::XMLElement* element = XmlItem::getXmlChildeEleByName(m_xmlelement, memberName);
        if (element == nullptr)
        {
            return false;
        }

        tinyxml2::XMLAttribute* attr = XmlItem::getXmlChildAttrByName(element, attrName);
        if (attr == nullptr)
        {
            return false;
        }

        if (attr->Value())
        {
            dataValue = StringUtils::string2Pod<T>(attr->Value());
            return true;
        }
        // 当节点没有值时,设置值为空
        dataValue = StringUtils::string2Pod<T>("");
        return true;
    }

    /*********************************
    * @函数描述: 将obj转为xml字符串
    * @param {Value} &xmlValue xml值
    * @param {bool} &boolValue 转化的具体的string值
    * @返回值 : void
    **********************************/
    template <typename T>
    void val2Element(const T& member)
    {
        m_xmlelement->SetText((StringUtils::pod2String<T>(member)).c_str());
    }

    /*********************************
    * @函数描述: 将obj转为xml字符串
    * @param {Value} &xmlValue xml值
    * @param {bool} &boolValue 转化的具体的string值
    * @返回值 : void
    **********************************/
    template <typename T>
    void val2ElementEx(const T& member)
    {
        std::string memVal = StringUtils::pod2String<T>(member);
        m_xmlelement->SetText(memVal.c_str(), true);
    }

    /*********************************
    * @函数描述: 将obj转为xml字符串
    * @param {Value} &xmlValue xml值
    * @param {bool} &boolValue 转化的具体的string值
    * @返回值 : void
    **********************************/
    template <typename T>
    void val2Element(const std::string& memberName, const T& member)
    {
        tinyxml2::XMLElement* element = XmlItem::getXmlChildeEleByName(m_xmlelement, memberName);
        if (element)
        {
            element->SetText((StringUtils::pod2String<T>(member)).c_str());
        }
        else
        {
            tinyxml2::XMLElement* element = m_xmldocument->NewElement(memberName.c_str());
            element->SetText((StringUtils::pod2String<T>(member)).c_str());
            m_xmlelement->InsertEndChild(element);
        }
    }

    /*********************************
    * @函数描述: 将obj转为xml字符串
    * @param {Value} &xmlValue xml值
    * @param {bool} &boolValue 转化的具体的string值
    * @返回值 : void
    **********************************/
    template <typename T>
    void val2ElementEx(const std::string& memberName, const T& member)
    {
        tinyxml2::XMLElement* element = XmlItem::getXmlChildeEleByName(m_xmlelement, memberName);
        // 给内容添加容器
        std::string memVal = StringUtils::pod2String<T>(member);
        if (element)
        {
            element->SetText(memVal.c_str(), true);
        }
        else
        {
            tinyxml2::XMLElement* element = m_xmldocument->NewElement(memberName.c_str());
            element->SetText(memVal.c_str(), true);
            m_xmlelement->InsertEndChild(element);
        }
    }

    /*********************************
    * @函数描述: 将obj转为xml字符串
    * @param {Value} &xmlValue xml值
    * @param {bool} &boolValue 转化的具体的string值
    * @返回值 : void
    **********************************/
    template <typename T>
    void val2Attribute(const std::string& attrName, const T& member)
    {
        m_xmlelement->SetAttribute(attrName.c_str(), StringUtils::pod2String<T>(member).c_str());
    }

    /*********************************
    * @函数描述: 将obj转为xml字符串
    * @param {Value} &xmlValue xml值
    * @param {bool} &boolValue 转化的具体的string值
    * @返回值 : void
    **********************************/
    template <typename T>
    void val2Attribute(const std::string& memberName, const std::string& attrName, const T& member)
    {
        tinyxml2::XMLElement* element = XmlItem::getXmlChildeEleByName(m_xmlelement, memberName);
        if (element)
        {
            element->SetAttribute(attrName.c_str(), StringUtils::pod2String<T>(member).c_str());
        }
        else
        {
            tinyxml2::XMLElement* element = m_xmldocument->NewElement(memberName.c_str());
            element->SetAttribute(attrName.c_str(), StringUtils::pod2String<T>(member).c_str());
            m_xmlelement->InsertEndChild(element);
        }
    }
protected:

    /**
     * @功能描述: 将xml字符串转为对象值
     * @入参参数: 对象的key值, 对象值
     * @返回值: 成功或者失败
     */
    bool xml2Object(const std::string& memberName, JumpXml& objValue);


    /**
     * @功能描述: 将obj转为xml字符串
     * @入参参数: 对象的key值, 对象值
     * @返回值: 成功或者失败
     */
    bool object2Xml(const std::string& memberName, JumpXml& objValue);

protected:

    /*********************************
    @函数描述: 将xml字符串转为对象值
    @入参 : void
    @返回值 : xml 字串str
    **********************************/
    template <typename T>
    bool element2ValVec(const std::string& memberName, std::vector<T>& memberVec)
    {
        if (!memberVec.empty())
        {
            memberVec.clear();
        }

        // 遍历节点
        for (tinyxml2::XMLElement* element = m_xmlelement->FirstChildElement();
            NULL != element;
            element = element->NextSiblingElement())
        {
            if (NULL == element->Name() || NULL == element->GetText())
            {
                continue;
            }
            // 查询列表中所有节点
            if (memberName == element->Name())
            {
                T dataValue = StringUtils::string2Pod<T>(element->GetText());
                memberVec.push_back(dataValue);
            }
        }
        return true;
    }

    /*********************************
    @函数描述: 将对象转为 xml 字串
    @入参 : void
    @返回值 : xml 字串str
    **********************************/
    template <typename T>
    bool valVec2Element(const std::string& memberName, std::vector<T>& memberVec)
    {
        // 剔除节点历史数据保证数据不会重复
        tinyxml2::XMLElement* element = m_xmlelement->FirstChildElement();
        while (element)
        {
            if (NULL == element->Name())
            {
                continue;
            }
            if (memberName == element->Name())
            {
                m_xmlelement->DeleteChild(element);
            }
            element = element->NextSiblingElement();
        }
        // 遍历新建节点
        for (const auto& val : memberVec)
        {
            tinyxml2::XMLElement* element = m_xmldocument->NewElement(memberName.c_str());
            element->SetText(StringUtils::pod2String<T>(val).c_str());
            m_xmlelement->InsertEndChild(element);
        }

        return false;
    }

    /*********************************
    @函数描述: 将对象转为 xml 字串
    @入参 : void
    @返回值 : xml 字串str
    **********************************/
    template <typename T>
    bool valVec2ElementEx(const std::string& memberName, std::vector<T>& memberVec)
    {
        // 剔除节点历史数据保证数据不会重复
        tinyxml2::XMLElement* element = m_xmlelement->FirstChildElement();
        while (element)
        {
            if (NULL == element->Name())
            {
                continue;
            }
            if (memberName == element->Name())
            {
                m_xmlelement->DeleteChild(element);
            }
            element = element->NextSiblingElement();
        }
        // 遍历新建节点
        for (const auto& val : memberVec)
        {
            tinyxml2::XMLElement* element = m_xmldocument->NewElement(memberName.c_str());
            element->SetText(StringUtils::pod2String<T>(val).c_str(), true);
            m_xmlelement->InsertEndChild(element);
        }

        return false;
    }

protected:

    /*********************************
    @函数描述: 将xml字符串转为对象值
    @入参 : void
    @返回值 : xml 字串str
    **********************************/
    template <typename T>
    bool element2ObjVec(const std::string& memberName, std::vector<T>& memberVec)
    {
        if (!memberVec.empty())
        {
            memberVec.clear();
        }

        // 遍历节点
        for (tinyxml2::XMLElement* element = m_xmlelement->FirstChildElement();
            NULL != element;
            element = element->NextSiblingElement())
        {
            if (NULL == element->Name())
            {
                continue;
            }
            // 查询列表中所有节点
            if (memberName == element->Name())
            {
                //将xml字符串转为对象值
                T objValue;
                objValue.setXMLDocument(m_xmldocument);
                objValue.setXMLElement(element);
                objValue.xml2Val();
                memberVec.push_back(objValue);
            }
        }
        return true;
    }

    /*********************************
    @函数描述: 将对象转为 xml 字串
    @入参 : void
    @返回值 : xml 字串str
    **********************************/
    template <typename T>
    bool objVec2Element(const std::string& memberName, std::vector<T>& memberVec)
    {
        // 剔除节点历史数据保证数据不会重复
        tinyxml2::XMLElement* element = m_xmlelement->FirstChildElement();
        while (element)
        {
            if (NULL == element->Name())
            {
                continue;
            }
            if (memberName == element->Name())
            {
                m_xmlelement->DeleteChild(element);
            }
            element = element->NextSiblingElement();
        }
        // 遍历新建节点
        for (auto& member : memberVec)
        {
            tinyxml2::XMLElement* element = m_xmldocument->NewElement(memberName.c_str());
            //是指子集的属性
            member.setXMLDocument(m_xmldocument);
            member.setXMLElement(element);
            member.val2Xml();
            m_xmlelement->InsertEndChild(element);
        }

        return false;
    }

private:

    /**
     * @功能描述: 定义将json转为对应的值 虚函数
     * @入参参数: void
     * @返回值: void
     */
    virtual void xml2Val() {}

    /**
     * @功能描述: 定义将对应的值转为json 虚函数
     * @入参参数: void
     * @返回值: void
     */
    virtual void val2Xml() {}
private:
    tinyxml2::XMLElement* m_xmlelement;       // 当前节点
    tinyxml2::XMLDocument* m_xmldocument;      // xml解析器
    std::string m_rootName = "Root"; // 根节点名称
};

#define XML_DATA(str) XmlItem::addStringWithData((str))
#endif
